namespace ScrumSprintMonitor.Testbench.Impl
{
	#region #using Directives

	using System;
	using System.ComponentModel;
	using System.Threading;

	using ScrumSprintMonitor.Infrastructure;

	#endregion

	public class TestWorkItemStore : IWorkItemStore,
	                                 IDisposable
	{
		private readonly SynchronizationContext syncContext;
		private readonly Timer timer;
		private readonly BindingList<IWorkItemAdapter> workItems;
		private bool changed = true;

		#region Implementation of IWorkItemStore

		public void Refresh()
		{
			RaiseWorkItemsRefreshed();

			if (this.changed)
			{
				RaiseWorkItemsUpdated();
			}
		}

		public BindingList<IWorkItemAdapter> WorkItems
		{
			get { return this.workItems; }
		}

		public event EventHandler<RefreshResultsEventArgs> WorkItemsRefreshed;

		public event EventHandler WorkItemsUpdated;

		#endregion

		public TestWorkItemStore(BindingList<IWorkItemAdapter> workItems)
		{
			this.timer = new Timer(OnNotifyUpdated, null, Timeout.Infinite, Timeout.Infinite);
			this.syncContext = SynchronizationContext.Current;

			this.workItems = workItems;
			this.workItems.ListChanged += workItems_ListChanged;
		}

		private void OnNotifyUpdated(object state)
		{
			this.syncContext.Post(RaiseWorkItemsUpdated, null);
		}

		private void RaiseWorkItemsUpdated(object state)
		{
			try
			{
				RaiseWorkItemsUpdated();
			}
			catch (RemainingWorkNotEstimatedException)
			{
				// Ignore exception
			}
		}

		private void RaiseWorkItemsRefreshed()
		{
			if (WorkItemsRefreshed != null)
			{
				WorkItemsRefreshed(this, new RefreshResultsEventArgs(null));
			}
		}

		private void RaiseWorkItemsUpdated()
		{
			if (WorkItemsUpdated != null)
			{
				WorkItemsUpdated(this, EventArgs.Empty);
			}

			this.changed = false;
		}

		private void workItems_ListChanged(object sender, ListChangedEventArgs e)
		{
			this.changed = true;

			this.timer.Change(3000, Timeout.Infinite);
		}

		#region Implementation of IDisposable

		public void Dispose()
		{
			this.timer.Dispose();
		}

		#endregion
	}
}